package com.fwj.authorization.security;


import com.fwj.authorization.security.constants.CommonConstants;
import com.fwj.authorization.security.constants.SecurityConstants;
import com.fwj.authorization.tenant.TenantIdContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * RBAC 所有的请求都会到这里做权限校验
 *
 * @author fwj
 * @date 2025/03/18
 */
@Slf4j
@Component("custom")
public class CustomAuthorityService {

    @Value("${security.oauth2.ignore-urls}")
    private List<String> ignoreUrl;
    @Value("${custom.tenant.enable}")
    private boolean tenantEnable;

    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        String uri = request.getRequestURI();
        for (String startStr : ignoreUrl) {
            startStr = startStr.replaceAll("\\*", "");
            if (uri.startsWith(startStr)) {
                return true;
            }
        }
        log.info("当前请求地址:{}", uri);
        if (tenantEnable && StringUtils.isEmpty(TenantIdContext.getCurrentTenantId())) {
            // 取出租户ID，存入当前租户上下文对象
            String tenantId = request.getHeader(CommonConstants.TENANT_ID_HEADER);
            if (tenantId == null) {
                throw new RuntimeException("租户ID不能为空");
            }
            TenantIdContext.setCurrentTenantId(tenantId);
        }
        List<SimpleGrantedAuthority> grantedAuthorities = (List<SimpleGrantedAuthority>) authentication.getAuthorities();
        String token = request.getHeader(SecurityConstants.AUTHORIZATION_HEADER);
        // 未登录&未提供token,直接拦截
        if (token == null) {
            return false;
        }
        token = token.replace(SecurityConstants.AUTHORIZATION_TOKEN_PREFIX, "");
        /*
          未登录 --> token验证通过:重构系统用户authentication,继续验权限  --> token验证不通过：拦截
         */
        if (grantedAuthorities != null) {
            SimpleGrantedAuthority roleName = grantedAuthorities.get(0);
            if (SecurityConstants.ROLE_ANONYMOUS.equals(roleName.getAuthority())) {
                // 未登录&提供token但token未通过验证
                LoginUser loginUser = JwtTokenUtil.getUserFromToken(token);
                if (loginUser == null) {
                    return false;
                } else {
                    Authentication refreshAuthentication = refreshAuthentication(loginUser, request);
                    // 更新 authentication
                    grantedAuthorities = (List<SimpleGrantedAuthority>) refreshAuthentication.getAuthorities();
                }
            }
            LoginUser loginUser = JwtTokenUtil.getUserFromToken(token);
            List<String> rolePrem = loginUser.getRolePrem();
            // 登录人拥有的角色
            String[] roleCodes = grantedAuthorities.stream().map(SimpleGrantedAuthority::getAuthority).toArray(String[]::new);
            // 该用户的角色与该URL所需要的角色有重合
            for (String roleCode : roleCodes) {
                if (rolePrem.contains(roleCode + "[" + uri + "]")) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 重建该用户当前的权限
     *
     * @param loginUser 用户信息
     */
    private Authentication refreshAuthentication(LoginUser loginUser, HttpServletRequest request) {
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, loginUser.getPassword(), loginUser.getAuthorities());
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return authentication;
    }

    @Bean
    private AntPathMatcher antPathMatcher() {
        return new AntPathMatcher();
    }
}
